mod color;
pub use color::Color;
use image::{ImageBuffer, Rgba};
use std::path::Path;

pub struct Canvas {
    buffer: ImageBuffer<Rgba<u8>, Vec<u8>>,
}

impl Canvas {
    pub fn new(width: usize, height: usize) -> Self {
        Self { buffer: ImageBuffer::new(width as u32, height as u32) }
    }

    pub fn width(&self) -> usize {
        self.buffer.width() as usize
    }

    pub fn height(&self) -> usize {
        self.buffer.height() as usize
    }

    pub fn fill(&mut self, color: Color) {
        for pixel in self.buffer.pixels_mut() {
            *pixel = color.0;
        }
    }

    pub fn line(&mut self, color: Color, x0: i32, y0: i32, x1: i32, y1: i32) {
        let dx = (x1 - x0).abs();
        let dy = (y1 - y0).abs();
        let sx = if x0 < x1 { 1 } else { -1 };
        let sy = if y0 < y1 { 1 } else { -1 };
        let mut err = dx - dy;
    
        let mut x = x0;
        let mut y = y0;
    
        loop {
            self.point(color, x, y);
    
            if x == x1 && y == y1 {
                break;
            }
    
            let e2 = err * 2;
            if e2 > -dy {
                err -= dy;
                x += sx;
            }
            if e2 < dx {
                err += dx;
                y += sy;
            }
        }
    }
    

    pub fn rectangle(&mut self, color: Color, x: i32, y: i32, width: i32, height: i32) {
        for i in y..y + height {
            for j in x..x + width {
                self.point(color, j, i);
            }
        }
    }

    pub fn circle(&mut self, color: Color, cx: i32, cy: i32, r: i32) {
        for y in -r..=r {
            for x in -r..=r {
                if x * x + y * y <= r * r {
                    self.point(color, cx + x, cy + y);
                }
            }
        }
    }

    pub fn triangle(&mut self, color: Color, x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32) {
        let ((x1, y1), (x2, y2), (x3, y3)) = Canvas::sort_triangle_points_by_y(x1, y1, x2, y2, x3, y3);

        let dx12 = x2 - x1;
        let dy12 = y2 - y1;
        let dx13 = x3 - x1;
        let dy13 = y3 - y1;

        for y in y1..=y2 {
            let s1 = if dy12 != 0 { (y - y1) * dx12 / dy12 + x1 } else { x1 };
            let s2 = if dy13 != 0 { (y - y1) * dx13 / dy13 + x1 } else { x1 };
            let (s1, s2) = (s1.min(s2), s1.max(s2));
            for x in s1..=s2 {
                self.point(color, x, y);
            }
        }

        let dx32 = x2 - x3;
        let dy32 = y2 - y3;
        let dx31 = x1 - x3;
        let dy31 = y1 - y3;

        for y in y2+1..=y3 {
            let s1 = if dy32 != 0 { (y - y3) * dx32 / dy32 + x3 } else { x3 };
            let s2 = if dy31 != 0 { (y - y3) * dx31 / dy31 + x3 } else { x3 };
            let (s1, s2) = (s1.min(s2), s1.max(s2));
            for x in s1..=s2 {
                self.point(color, x, y);
            }
        }
    }

    pub fn point(&mut self, color: Color, x: i32, y: i32) {
        if x >= 0 && x < self.buffer.width() as i32 && y >= 0 && y < self.buffer.height() as i32 {
            *self.buffer.get_pixel_mut(x as u32, y as u32) = color.0;
        }
    }

    pub fn save_png<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
        match self.buffer.save(path) {
            Ok(()) => Ok(()),
            Err(err) => Err(err.to_string()),
        }
    }
}

impl Canvas {
    fn sort_triangle_points_by_y(x0: i32, y0: i32, x1: i32, y1: i32, x2: i32, y2: i32) 
        -> ((i32, i32), (i32, i32), (i32, i32)) 
    {
        let mut vertices = [(x0, y0), (x1, y1), (x2, y2)];
        vertices.sort_by(|a, b| a.1.cmp(&b.1));
        (vertices[0], vertices[1], vertices[2])
    }
}

#[cfg(test)]
mod test {
    #[allow(unused)]
    use super::*;

    #[test]
    fn test_fill() {
        let mut canvas = Canvas::new(400, 300);
        canvas.fill(Color::rgb(100, 100, 255));
        canvas.save_png("./image/test_fill.png").unwrap();
    }

    #[test]
    fn test_line() {
        let mut canvas = Canvas::new(400, 300);
        canvas.fill(Color::light_gray());
        canvas.line(Color::blue(), 10, 10, 299, 50);
        canvas.line(Color::red(), 10, 288, 50, 50);
        canvas.line(Color::white(), 200, 100, 50, 50);
        canvas.line(Color::green(), 150, 0, 150, 100);
        canvas.line(Color::red(), 150, 100, 250, 100);
        canvas.save_png("./image/test_line.png").unwrap();
    }

    #[test]
    fn test_rectangle() {
        let mut canvas = Canvas::new(400, 300);
        canvas.fill(Color::light_gray());
        canvas.rectangle(Color::blue(), 10, 10, 100, 50);
        canvas.rectangle(Color::green(), 90, 50, 100, 50);
        canvas.save_png("./image/test_rectangle.png").unwrap();
    }

    #[test]
    fn test_circle() {
        let mut canvas = Canvas::new(400, 300);
        canvas.fill(Color::light_gray());
        canvas.rectangle(Color::blue(), 10, 10, 100, 50);
        canvas.circle(Color::red(), 100, 100, 78);
        canvas.save_png("./image/test_circle.png").unwrap();
    }

    #[test]
    fn test_triangle() {
        let mut canvas = Canvas::new(400, 300);
        canvas.fill(Color::light_gray());
        canvas.triangle(Color::blue(), 10, 10, 10, 100, 100, 10);
        canvas.triangle(Color::green(), 100, 100, 200, 200, 100, 200);
        canvas.save_png("./image/test_triangle.png").unwrap();
    }
}
